<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha256-Z1K5uhUaJXA7Ll0XrZ/0JhX4lAtZFpT6jkKrEDT0drU=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"example.com","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.14.1","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":-1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta name="description" content="PipelinePrerequisites安装Pipeline插件在你想创建一条流水线的时候，有可能发现并没有这个UI入口，原因是还没有下载相关插件，所以可以下载插件****，这是一整套和流水线相关的插件。（还有 BlueOcean等其他流水线相关的插件，这里先不使用，只使用classic的pipeline）。 下载完记得重启docker-compose restart。 创建流水线Jenkins">
<meta property="og:type" content="article">
<meta property="og:title" content="使用Jenkins的pipeline实现CI&#x2F;CD">
<meta property="og:url" content="http://example.com/2022/05/07/Sdu-Devops-02/index.html">
<meta property="og:site_name" content="JsyBlog">
<meta property="og:description" content="PipelinePrerequisites安装Pipeline插件在你想创建一条流水线的时候，有可能发现并没有这个UI入口，原因是还没有下载相关插件，所以可以下载插件****，这是一整套和流水线相关的插件。（还有 BlueOcean等其他流水线相关的插件，这里先不使用，只使用classic的pipeline）。 下载完记得重启docker-compose restart。 创建流水线Jenkins">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2022-05-07T13:00:00.000Z">
<meta property="article:modified_time" content="2022-06-08T11:01:34.596Z">
<meta property="article:author" content="SongyangJi">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="http://example.com/2022/05/07/Sdu-Devops-02/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":true,"permalink":"http://example.com/2022/05/07/Sdu-Devops-02/","path":"2022/05/07/Sdu-Devops-02/","title":"使用Jenkins的pipeline实现CI/CD"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>使用Jenkins的pipeline实现CI/CD | JsyBlog</title>
  








  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">JsyBlog</p>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#Pipeline"><span class="nav-number">1.</span> <span class="nav-text">Pipeline</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Prerequisites"><span class="nav-number">1.1.</span> <span class="nav-text">Prerequisites</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AE%89%E8%A3%85Pipeline%E6%8F%92%E4%BB%B6"><span class="nav-number">1.1.1.</span> <span class="nav-text">安装Pipeline插件</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%88%9B%E5%BB%BA%E6%B5%81%E6%B0%B4%E7%BA%BF"><span class="nav-number">1.2.</span> <span class="nav-text">创建流水线</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Jenkinsfile%E6%A8%A1%E6%9D%BF"><span class="nav-number">1.2.1.</span> <span class="nav-text">Jenkinsfile模板</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BB%8EGitLab%E6%8B%89%E5%8F%96%E4%BB%A3%E7%A0%81"><span class="nav-number">1.3.</span> <span class="nav-text">从GitLab拉取代码</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Git-Parameter"><span class="nav-number">1.3.1.</span> <span class="nav-text">Git Parameter</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BD%BF%E7%94%A8WebHooks%E5%AE%9E%E7%8E%B0push%E5%88%86%E6%94%AF%E8%87%AA%E5%8A%A8build"><span class="nav-number">1.3.2.</span> <span class="nav-text">使用WebHooks实现push分支自动build</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BD%BF%E7%94%A8Jenkins%E5%AE%B9%E5%99%A8%E5%86%85%E7%9A%84Maven%E6%89%93%E5%8C%85"><span class="nav-number">1.4.</span> <span class="nav-text">使用Jenkins容器内的Maven打包</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BD%BF%E7%94%A8Jenkins%E5%AE%B9%E5%99%A8%E5%86%85%E7%9A%84Docker%E5%88%B6%E4%BD%9C%E8%87%AA%E5%AE%9A%E4%B9%89%E9%95%9C%E5%83%8F"><span class="nav-number">1.5.</span> <span class="nav-text">使用Jenkins容器内的Docker制作自定义镜像</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%B0%86%E8%87%AA%E5%AE%9A%E4%B9%89%E9%95%9C%E5%83%8F%E6%8E%A8%E9%80%81%E5%88%B0Harbor"><span class="nav-number">1.6.</span> <span class="nav-text">将自定义镜像推送到Harbor</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%80%9A%E8%BF%87SSH%E9%80%9A%E7%9F%A5%E7%9B%AE%E6%A0%87%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%83%A8%E7%BD%B2"><span class="nav-number">1.7.</span> <span class="nav-text">通过SSH通知目标服务器部署</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%80%9A%E8%BF%87SSH%E9%80%9A%E7%9F%A5%E7%9B%AE%E6%A0%87%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%83%A8%E7%BD%B2%EF%BC%88%E9%83%A8%E7%BD%B2%E7%AB%AF%E4%BD%BF%E7%94%A8go%E9%83%A8%E7%BD%B2%EF%BC%89"><span class="nav-number">1.8.</span> <span class="nav-text">通过SSH通知目标服务器部署（部署端使用go部署）</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%83%A8%E7%BD%B2%E7%A8%8B%E5%BA%8F"><span class="nav-number">1.8.1.</span> <span class="nav-text">部署程序</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%9B%91%E6%8E%A7%E7%A8%8B%E5%BA%8F"><span class="nav-number">1.8.2.</span> <span class="nav-text">监控程序</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%9E%84%E5%BB%BA%E8%84%9A%E6%9C%AC"><span class="nav-number">1.8.3.</span> <span class="nav-text">构建脚本</span></a></li></ol></li></ol></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">SongyangJi</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">251</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">45</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">109</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2022/05/07/Sdu-Devops-02/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="使用Jenkins的pipeline实现CI/CD | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          使用Jenkins的pipeline实现CI/CD
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-05-07 21:00:00" itemprop="dateCreated datePublished" datetime="2022-05-07T21:00:00+08:00">2022-05-07</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-06-08 19:01:34" itemprop="dateModified" datetime="2022-06-08T19:01:34+08:00">2022-06-08</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/DevOps/" itemprop="url" rel="index"><span itemprop="name">DevOps</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <h1 id="Pipeline"><a href="#Pipeline" class="headerlink" title="Pipeline"></a>Pipeline</h1><h2 id="Prerequisites"><a href="#Prerequisites" class="headerlink" title="Prerequisites"></a>Prerequisites</h2><h3 id="安装Pipeline插件"><a href="#安装Pipeline插件" class="headerlink" title="安装Pipeline插件"></a>安装Pipeline插件</h3><p>在你想创建一条流水线的时候，有可能发现并没有这个UI入口，原因是还没有下载相关插件，所以可以下载插件****，这是一整套和流水线相关的插件。（还有 BlueOcean等其他流水线相关的插件，这里先不使用，只使用classic的pipeline）。</p>
<p>下载完记得重启<code>docker-compose restart</code>。</p>
<h2 id="创建流水线"><a href="#创建流水线" class="headerlink" title="创建流水线"></a>创建流水线</h2><h3 id="Jenkinsfile模板"><a href="#Jenkinsfile模板" class="headerlink" title="Jenkinsfile模板"></a>Jenkinsfile模板</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">pipeline &#123;</span><br><span class="line">    agent any</span><br><span class="line"></span><br><span class="line">    stages &#123;</span><br><span class="line">        stage(&#x27;Stage 1: Fetch code from git&#x27;) &#123;</span><br><span class="line">            steps &#123;</span><br><span class="line">                echo &#x27;Stage 1: Fetch code from git -- SUCCESS&#x27;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        stage(&#x27;Stage 2: Build the project using maven&#x27;) &#123;</span><br><span class="line">            steps &#123;</span><br><span class="line">                echo &#x27;Stage 2: Build the project using maven -- SUCCESS&#x27;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        stage(&#x27;Stage 3: Make a custom image using docker&#x27;) &#123;</span><br><span class="line">            steps &#123;</span><br><span class="line">                echo &#x27;Stage 3: Make a custom image using docker -- SUCCESS&#x27;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        stage(&#x27;Stage 4: Push image to Harbor&#x27;) &#123;</span><br><span class="line">            steps &#123;</span><br><span class="line">                echo &#x27;Stage 4: Push image to Harbor -- SUCCESS&#x27;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        stage(&#x27;Stage 5: Publish over SSH&#x27;) &#123;</span><br><span class="line">            steps &#123;</span><br><span class="line">                echo &#x27;Stage 5: Publish over SSH -- SUCCESS&#x27;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="从GitLab拉取代码"><a href="#从GitLab拉取代码" class="headerlink" title="从GitLab拉取代码"></a>从GitLab拉取代码</h2><p>流水线片段脚本：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">checkout([$class: &#x27;GitSCM&#x27;, branches: [[name: &#x27;$&#123;branch_tag&#125;&#x27;]], extensions: [], userRemoteConfigs: [[url: &#x27;http://211.87.224.233:8929/sdu-weblab/springboot-helloworld.git&#x27;]]])</span><br></pre></td></tr></table></figure>
<p>其中<code>branch_tag</code>为git参数（此项功能需要 Git Parameter 插件）。</p>
<p>stage为：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">stage(&#x27;Stage 1: Fetch code from git&#x27;) &#123;</span><br><span class="line">    steps &#123;</span><br><span class="line">        checkout([$class: &#x27;GitSCM&#x27;, branches: [[name: &#x27;$&#123;branch_tag&#125;&#x27;]], extensions: [], userRemoteConfigs: [[url: &#x27;http://211.87.224.233:8929/sdu-weblab/springboot-helloworld.git&#x27;]]])</span><br><span class="line">        echo &#x27;Stage 1: Fetch code from git -- SUCCESS&#x27;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h3 id="Git-Parameter"><a href="#Git-Parameter" class="headerlink" title="Git Parameter"></a>Git Parameter</h3><p>该插件允许您在构建中分配<strong>branch, tag, pull request or revision number</strong>作为参数。</p>
<p>此插件将从你的项目中读取 GIT SCM 配置。<br>这个插件直接使用了<a target="_blank" rel="noopener" href="https://plugins.jenkins.io/git/">Git Plugin</a>和<a target="_blank" rel="noopener" href="https://plugins.jenkins.io/git-client/">Git Client Plugin</a>。</p>
<p>git 拉取分支：</p>
<p>checkout: Check out from version control</p>
<p><a target="_blank" rel="noopener" href="http://211.87.224.233:18080/">http://211.87.224.233:18080/</a></p>
<p><a target="_blank" rel="noopener" href="https://plugins.jenkins.io/git-parameter">https://plugins.jenkins.io/git-parameter</a></p>
<h3 id="使用WebHooks实现push分支自动build"><a href="#使用WebHooks实现push分支自动build" class="headerlink" title="使用WebHooks实现push分支自动build"></a>使用WebHooks实现push分支自动build</h3><p><del><a target="_blank" rel="noopener" href="http://211.87.224.233:8080/project/pipeline-springboot-helloworld">http://211.87.224.233:8080/project/pipeline-springboot-helloworld</a></del></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Hook executed successfully but returned HTTP 404 &lt;!doctype html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;&lt;title&gt;HTTP Status 404 – Not Found&lt;/title&gt;&lt;style type=&quot;text/css&quot;&gt;body &#123;font-family:Tahoma,Arial,sans-serif;&#125; h1, h2, h3, b &#123;color:white;background-color:#525D76;&#125; h1 &#123;font-size:22px;&#125; h2 &#123;font-size:16px;&#125; h3 &#123;font-size:14px;&#125; p &#123;font-size:12px;&#125; a &#123;color:black;&#125; .line &#123;height:1px;background-color:#525D76;border:none;&#125;&lt;/style&gt;&lt;/head&gt;&lt;body&gt;&lt;h1&gt;HTTP Status 404 – Not Found&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;</span><br></pre></td></tr></table></figure>



<ol>
<li>下载插件<strong>Gitlab</strong>插件</li>
<li>在gitlab上设置Jenkins里插件生成的webhook，并添加secret token。此次webhook token为<a target="_blank" rel="noopener" href="http://jenkins:8080/project/pipeline-springboot-helloworld">http://jenkins:8080/project/pipeline-springboot-helloworld</a></li>
</ol>
<h2 id="使用Jenkins容器内的Maven打包"><a href="#使用Jenkins容器内的Maven打包" class="headerlink" title="使用Jenkins容器内的Maven打包"></a>使用Jenkins容器内的Maven打包</h2><p>打包命令</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$</span><span class="language-bash">&#123;MAVEN_HOME&#125;/bin/mvn clean package -Dmaven.test.skip=<span class="literal">true</span> <span class="comment"># 容器内的$&#123;MAVEN_HOME&#125;,不是宿主机的,在这里为/opt/maven</span></span></span><br></pre></td></tr></table></figure>



<p>片段生成器生成的语句:</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sh &#x27;/opt/maven/bin/mvn clean package -Dmaven.test.skip=true&#x27;</span><br></pre></td></tr></table></figure>





<p>stage为：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">stage(&#x27;Stage 2: Build the project using maven&#x27;) &#123;</span><br><span class="line">    steps &#123;</span><br><span class="line">        // 下面两条shell作debug用，后面删去</span><br><span class="line">        sh &#x27;pwd&#x27;</span><br><span class="line">        sh &#x27;ls -al&#x27;</span><br><span class="line">        sh &#x27;/opt/maven/bin/mvn clean package -Dmaven.test.skip=true&#x27;</span><br><span class="line">        echo &#x27;Stage 2: Build the project using maven -- SUCCESS&#x27;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>日志：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line">+ pwd</span><br><span class="line">/var/jenkins_home/workspace/pipeline-springboot-helloworld</span><br><span class="line">[Pipeline] sh</span><br><span class="line">+ ls -al</span><br><span class="line">total 8</span><br><span class="line">drwxr-xr-x 5 jenkins jenkins  100 May  6 10:11 .</span><br><span class="line">drwxr-xr-x 4 jenkins jenkins   98 May  6 10:09 ..</span><br><span class="line">drwxr-xr-x 8 jenkins jenkins  210 May  6 10:13 .git</span><br><span class="line">-rw-r--r-- 1 jenkins jenkins  387 May  5 15:35 .gitignore</span><br><span class="line">-rw-r--r-- 1 jenkins jenkins 1437 May  5 15:35 pom.xml</span><br><span class="line">drwxr-xr-x 4 jenkins jenkins   42 May  5 15:35 src</span><br><span class="line">drwxr-xr-x 6 jenkins jenkins  199 May  6 10:12 target</span><br><span class="line">[Pipeline] sh</span><br><span class="line">+ /opt/maven/bin/mvn clean package -Dmaven.test.skip=true</span><br><span class="line">[INFO] Scanning for projects...</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] -----------------------&lt; com.sduweb:helloworld &gt;------------------------</span><br><span class="line">[INFO] Building helloworld 0.0.1-SNAPSHOT</span><br><span class="line">[INFO] --------------------------------[ jar ]---------------------------------</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ helloworld ---</span><br><span class="line">[INFO] Deleting /var/jenkins_home/workspace/pipeline-springboot-helloworld/target</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) @ helloworld ---</span><br><span class="line">[INFO] Using &#x27;UTF-8&#x27; encoding to copy filtered resources.</span><br><span class="line">[INFO] Using &#x27;UTF-8&#x27; encoding to copy filtered properties files.</span><br><span class="line">[INFO] Copying 1 resource</span><br><span class="line">[INFO] Copying 0 resource</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ helloworld ---</span><br><span class="line">[INFO] Changes detected - recompiling the module!</span><br><span class="line">[INFO] Compiling 2 source files to /var/jenkins_home/workspace/pipeline-springboot-helloworld/target/classes</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] --- maven-resources-plugin:3.2.0:testResources (default-testResources) @ helloworld ---</span><br><span class="line">[INFO] Not copying test resources</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ helloworld ---</span><br><span class="line">[INFO] Not compiling test sources</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ helloworld ---</span><br><span class="line">[INFO] Tests are skipped.</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] --- maven-jar-plugin:3.2.2:jar (default-jar) @ helloworld ---</span><br><span class="line">[INFO] Building jar: /var/jenkins_home/workspace/pipeline-springboot-helloworld/target/helloworld-0.0.1-SNAPSHOT.jar</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] --- spring-boot-maven-plugin:2.6.4:repackage (repackage) @ helloworld ---</span><br><span class="line">[INFO] Replacing main artifact with repackaged archive</span><br><span class="line">[INFO] ------------------------------------------------------------------------</span><br><span class="line">[INFO] BUILD SUCCESS</span><br><span class="line">[INFO] ------------------------------------------------------------------------</span><br><span class="line">[INFO] Total time:  2.425 s</span><br><span class="line">[INFO] Finished at: 2022-05-06T10:14:04Z</span><br><span class="line">[INFO] ------------------------------------------------------------------------</span><br></pre></td></tr></table></figure>


<p>从上面的日志输出可以得到如下结论：</p>
<ol>
<li>对于Jenkins每一个流水线项目，Jenkins都会在存储在<code>/var/jenkins_home/workspace/$&#123;projectName&#125;</code></li>
<li>从gitlab拉取代码后存储在本流水线对应的目录下<code>/var/jenkins_home/workspace/$&#123;projectName&#125;</code></li>
<li>打完jar后存储在<code>/var/jenkins_home/workspace/$&#123;projectName&#125;/target/xxx.jar</code></li>
</ol>
<blockquote>
<p><strong>跳过单元测试</strong></p>
<ul>
<li><p>-DskipTests，不执行<a target="_blank" rel="noopener" href="https://so.csdn.net/so/search?q=%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B&spm=1001.2101.3001.7020">测试用例</a>，但编译测试用例类生成相应的class文件至target&#x2F;test-classes下。 </p>
</li>
<li><p>-Dmaven.test.skip&#x3D;true，不执行测试用例，也不编译测试用例类。</p>
</li>
</ul>
</blockquote>
<blockquote>
<p>参考<br><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/6f91752f3962">https://www.jianshu.com/p/6f91752f3962</a><br><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/6f57c322e50e">https://www.jianshu.com/p/6f57c322e50e</a></p>
</blockquote>
<h2 id="使用Jenkins容器内的Docker制作自定义镜像"><a href="#使用Jenkins容器内的Docker制作自定义镜像" class="headerlink" title="使用Jenkins容器内的Docker制作自定义镜像"></a>使用Jenkins容器内的Docker制作自定义镜像</h2><p>pwd: &#x2F;var&#x2F;jenkins_home&#x2F;workspace&#x2F;${projectName}<br>&#x2F;target&#x2F;xxx.jar</p>
<p>Dockerfile</p>
<figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> openjdk:<span class="number">8</span>-jdk-alpine</span><br><span class="line"><span class="keyword">ARG</span> JarFileName</span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> <span class="variable">$&#123;JarFileName&#125;</span> /opt/weblab/</span></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> /opt/weblab/</span></span><br><span class="line"><span class="keyword">CMD</span><span class="language-bash"> java -jar <span class="variable">$&#123;JarFileName&#125;</span></span></span><br></pre></td></tr></table></figure>



<p><del>执行shell</del></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cp -r /opt/docker_workdir ./</span><br><span class="line">cp ./target/*.jar ./docker_workdir</span><br><span class="line">docker build -t $&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125; ./docker_workdir --build-arg JarFileName=$(ls target -l | grep jar$ | awk &#x27;&#123;print $9&#125;&#x27;)</span><br></pre></td></tr></table></figure>

<p><del>流水线片段</del></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sh &#x27;&#x27;&#x27;cp -r /opt/docker_workdir ./</span><br><span class="line">cp ./target/*.jar ./docker_workdir</span><br><span class="line">docker build -t $&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125; ./docker_workdir --build-arg JarFileName=$(ls target -l | grep jar$ | awk \&#x27;&#123;print $9&#125;\&#x27;)&#x27;&#x27;&#x27;</span><br></pre></td></tr></table></figure>



<p><del>stage: 为</del></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">stage(&#x27;Stage 3: Make a custom image using docker&#x27;) &#123;</span><br><span class="line">    steps &#123;</span><br><span class="line">        sh &#x27;&#x27;&#x27;</span><br><span class="line">        cp -r /opt/docker_workdir ./</span><br><span class="line">        cp ./target/*.jar ./docker_workdir</span><br><span class="line">        docker build -t $&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125; ./docker_workdir --build-arg JarFileName=$(ls target -l | grep jar$ | awk \&#x27;&#123;print $9&#125;\&#x27;)</span><br><span class="line">        &#x27;&#x27;&#x27;</span><br><span class="line">        echo &#x27;Stage 3: Make a custom image using docker -- SUCCESS&#x27;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>







<p>build_image.sh</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">cp -r /opt/docker_workdir ./</span><br><span class="line">cp ./target/*.jar ./docker_workdir</span><br><span class="line">jarFileName=$(ls *.jar)</span><br><span class="line">echo &quot;jar is $&#123;jarFileName&#125;&quot;</span><br><span class="line">docker build -t $&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125; ./docker_workdir --build-arg  JarFileName=&quot;$&#123;jarFileName&#125;&quot;</span><br></pre></td></tr></table></figure>











<h2 id="将自定义镜像推送到Harbor"><a href="#将自定义镜像推送到Harbor" class="headerlink" title="将自定义镜像推送到Harbor"></a>将自定义镜像推送到Harbor</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker login -u admin -p admin $HARBOR_URL</span><br></pre></td></tr></table></figure>



<p>Jenkinsfile:</p>
<p>增加以下环境变量</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">pipeline &#123;</span><br><span class="line">    agent any</span><br><span class="line">    </span><br><span class="line">		environment &#123;</span><br><span class="line">        harbor_user = &#x27;admin&#x27;</span><br><span class="line">        harbor_password = &#x27;admin&#x27;</span><br><span class="line">        harbor_url = &#x27;211.87.224.233:8930&#x27;</span><br><span class="line">        harbor_repo = &#x27;sdu-weblab&#x27;</span><br><span class="line">    &#125;</span><br><span class="line">    // ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>Shell</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">docker login -u $&#123;harborUser&#125; -p $&#123;harborPassword&#125; $&#123;harbor_url&#125;</span><br><span class="line">docker tag $&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125; $&#123;harbor_url&#125;/$&#123;harbor_repo&#125;/$&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125;</span><br><span class="line">docker rmi $(docker images -f &quot;dangling=true&quot; -q) -f</span><br><span class="line">docker push $&#123;harbor_url&#125;/$&#123;harbor_repo&#125;/$&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125;</span><br></pre></td></tr></table></figure>



<p><del>pipeline script:</del></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">sh &#x27;&#x27;&#x27;</span><br><span class="line">    docker login -u $&#123;harborUser&#125; -p $&#123;harborPassword&#125; $&#123;harbor_url&#125;</span><br><span class="line">    docker tag $&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125; $&#123;harbor_url&#125;/$&#123;harbor_repo&#125;/$&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125;</span><br><span class="line">    docker rmi $(docker images -f &quot;dangling=true&quot; -q) -f</span><br><span class="line">    docker push $&#123;harbor_url&#125;/$&#123;harbor_repo&#125;/$&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125;</span><br><span class="line"> &#x27;&#x27;&#x27;</span><br></pre></td></tr></table></figure>



<p><del>stage为：</del></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">stage(&#x27;Stage 4: Push image to Harbor&#x27;) &#123;</span><br><span class="line">    steps &#123;</span><br><span class="line">        sh &#x27;&#x27;&#x27;</span><br><span class="line">            docker login -u $&#123;harborUser&#125; -p $&#123;harborPassword&#125; $&#123;harbor_url&#125;</span><br><span class="line">            docker tag $&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125; $&#123;harbor_url&#125;/$&#123;harbor_repo&#125;/$&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125;</span><br><span class="line">            docker rmi $(docker images -f &quot;dangling=true&quot; -q) -f</span><br><span class="line">            docker push $&#123;harbor_url&#125;/$&#123;harbor_repo&#125;/$&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125;</span><br><span class="line">         &#x27;&#x27;&#x27;</span><br><span class="line">        echo &#x27;Stage 4: Push image to Harbor -- SUCCESS&#x27;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><del>改为执行本地的脚本。</del></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">harbor_user=$1</span><br><span class="line">harbor_password=$2</span><br><span class="line">harbor_url=$3</span><br><span class="line">harbor_repo=$4</span><br><span class="line">project=$5</span><br><span class="line">branch_tag=$6</span><br><span class="line"></span><br><span class="line">image_name=$&#123;harbor_url&#125;/$&#123;harbor_repo&#125;/$&#123;project&#125;:$&#123;branch_tag##*/&#125;</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除和镜像关联的容器(正常情况不会有，但这里是单机部署)</span></span><br><span class="line">container_id=$(docker ps -a | grep &quot;$&#123;image_name&#125;&quot; | awk &#x27;&#123;print $1&#125;&#x27;)</span><br><span class="line">if [ &quot;$&#123;container_id&#125;&quot; != &quot;&quot; ] ; then</span><br><span class="line">  echo &quot;Container $&#123;container_id&#125; of $&#123;image_name&#125; is running, now try to kill it...&quot;</span><br><span class="line">  docker stop &quot;$container_id&quot;</span><br><span class="line">  docker rm &quot;$container_id&quot;</span><br><span class="line">fi</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">todo 这样做其实还有是问题的，就是上面的 container_id 可能会有多个</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">shellcheck <span class="built_in">disable</span>=SC2086</span></span><br><span class="line">docker login -u &quot;$&#123;harbor_user&#125;&quot; -p &quot;$&#123;harbor_password&#125;&quot; $&#123;harbor_url&#125;</span><br><span class="line"></span><br><span class="line">docker tag &quot;$&#123;project&#125;&quot;:&quot;$&#123;branch_tag##*/&#125;&quot; &quot;$&#123;image_name&#125;&quot;</span><br><span class="line">docker rmi $(docker images -f &quot;dangling=true&quot; -q) -f</span><br><span class="line">docker push image_name</span><br></pre></td></tr></table></figure>



<p>Jenkins端shell：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/opt/docker_workdir/push_to_harbor.sh $harbor_user $harbor_password $harbor_url $harbor_repo $JOB_NAME $branch_tag</span><br></pre></td></tr></table></figure>

<p>流水线片段</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sh &#x27;/opt/docker_workdir/push_to_harbor.sh $harbor_user $harbor_password $harbor_url $harbor_repo $JOB_NAME $branch_tag&#x27;</span><br></pre></td></tr></table></figure>







<p>其中：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除悬挂镜像</span></span><br><span class="line">docker rmi $(docker images -f &quot;dangling=true&quot; -q) </span><br></pre></td></tr></table></figure>

<blockquote>
<p><em>docker中的none:none镜像是怎么回事？</em><br><a target="_blank" rel="noopener" href="https://projectatomic.io/blog/2015/07/what-are-docker-none-none-images/">https://projectatomic.io/blog/2015/07/what-are-docker-none-none-images/</a></p>
</blockquote>
<p>用 go 改写：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	<span class="string">&quot;context&quot;</span></span><br><span class="line">	<span class="string">&quot;flag&quot;</span></span><br><span class="line">	<span class="string">&quot;fmt&quot;</span></span><br><span class="line">	<span class="string">&quot;io&quot;</span></span><br><span class="line">	<span class="string">&quot;log&quot;</span></span><br><span class="line">	<span class="string">&quot;os&quot;</span></span><br><span class="line">	<span class="string">&quot;strings&quot;</span></span><br><span class="line">	<span class="string">&quot;time&quot;</span></span><br><span class="line"></span><br><span class="line">	<span class="string">&quot;github.com/docker/docker/api/types&quot;</span></span><br><span class="line">	<span class="string">&quot;github.com/docker/docker/client&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">	harborUser     <span class="type">string</span></span><br><span class="line">	harborPassword <span class="type">string</span></span><br><span class="line">	harborUrl      <span class="type">string</span></span><br><span class="line">	harborRepo     <span class="type">string</span></span><br><span class="line">	project        <span class="type">string</span></span><br><span class="line">	version        <span class="type">string</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">	logFileName <span class="type">string</span></span><br><span class="line">	logFile     *os.File</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">init</span><span class="params">()</span></span> &#123;</span><br><span class="line">	<span class="keyword">var</span> err <span class="type">error</span></span><br><span class="line">	logFileName = time.Now().Format(<span class="string">&quot;2006-01-02 15:04:05&quot;</span>) + <span class="string">&quot;[push_to_harbor].log&quot;</span></span><br><span class="line">	logFile, err = os.OpenFile(logFileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, <span class="number">0644</span>)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		<span class="built_in">panic</span>(err)</span><br><span class="line">	&#125;</span><br><span class="line">	log.SetOutput(logFile)</span><br><span class="line">	log.SetPrefix(<span class="string">&quot;[sdu-weblab-deploy]&quot;</span>)</span><br><span class="line">	log.SetFlags(log.LstdFlags | log.Lshortfile | log.LUTC)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	flag.StringVar(&amp;harborUser, <span class="string">&quot;harbor_User&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;harborPassword, <span class="string">&quot;harbor_password&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;harborUrl, <span class="string">&quot;harbor_url&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;harborRepo, <span class="string">&quot;harbor_repo&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;project, <span class="string">&quot;project&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;version, <span class="string">&quot;version&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	<span class="keyword">if</span> <span class="built_in">len</span>(harborUser) == <span class="number">0</span> || <span class="built_in">len</span>(harborPassword) == <span class="number">0</span> || <span class="built_in">len</span>(harborUrl) == <span class="number">0</span> ||</span><br><span class="line">		<span class="built_in">len</span>(harborRepo) == <span class="number">0</span> || <span class="built_in">len</span>(project) == <span class="number">0</span> || <span class="built_in">len</span>(version) == <span class="number">0</span> &#123;</span><br><span class="line">		log.Fatal(<span class="string">&quot;key args missing&quot;</span>)</span><br><span class="line">	&#125;</span><br><span class="line">	log.Printf(<span class="string">&quot;Notice: harbor_url=%s, harbor_repo=%s, project=%s, version=%s&quot;</span>,</span><br><span class="line">		harborUrl, harborRepo, project, version)</span><br><span class="line"></span><br><span class="line">	ctx := context.Background()</span><br><span class="line">	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;New docker client fail, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	version = getRealVersion(version)</span><br><span class="line">	imageName := getHarborImageName(harborUrl, harborRepo, project, version)</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 删除和镜像关联的容器(正常情况不会有，但这里是单机部署)</span></span><br><span class="line">	<span class="comment">// 1. check existing containers and remove it if so</span></span><br><span class="line">	log.Printf(<span class="string">&quot;Check if container of %s exists...\n&quot;</span>, imageName)</span><br><span class="line">	containers, err := cli.ContainerList(ctx, types.ContainerListOptions&#123;All: <span class="literal">true</span>&#125;)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;List all containers fail, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">var</span> timeOut = time.Second</span><br><span class="line">	<span class="keyword">for</span> _, c := <span class="keyword">range</span> containers &#123;</span><br><span class="line">		<span class="keyword">if</span> c.Image == imageName &#123;</span><br><span class="line">			cid := c.ID</span><br><span class="line">			err = cli.ContainerStop(ctx, cid, &amp;timeOut)</span><br><span class="line">			<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">				log.Printf(<span class="string">&quot;Warning: container.ID=%s stop failed&quot;</span>, cid)</span><br><span class="line">			&#125;</span><br><span class="line">			err = cli.ContainerRemove(ctx, cid, types.ContainerRemoveOptions&#123;Force: <span class="literal">true</span>&#125;)</span><br><span class="line">			<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">				log.Fatalf(<span class="string">&quot;Container.ID=%s remove failed&quot;</span>, cid)</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 2. tag by the rule of harbor</span></span><br><span class="line">	<span class="comment">// 2.1 check and remove outdated version images</span></span><br><span class="line">	log.Printf(<span class="string">&quot;Check if image %s exists...\n&quot;</span>, imageName)</span><br><span class="line">	imageList, err := cli.ImageList(ctx, types.ImageListOptions&#123;&#125;)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;List all images fail, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">for</span> _, image := <span class="keyword">range</span> imageList &#123;</span><br><span class="line">		<span class="keyword">for</span> _, repoTag := <span class="keyword">range</span> image.RepoTags &#123;</span><br><span class="line">			<span class="keyword">if</span> repoTag == imageName &#123;</span><br><span class="line">				log.Printf(<span class="string">&quot;Image=%s exists, next to remove it&quot;</span>, image.ID)</span><br><span class="line">				cli.ImageRemove(ctx, image.ID, types.ImageRemoveOptions&#123;</span><br><span class="line">					Force: <span class="literal">true</span>,</span><br><span class="line">				&#125;)</span><br><span class="line">				<span class="keyword">break</span></span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">// 2.2 tag</span></span><br><span class="line">	sourceImageName := getSourceImageName(harborRepo, project, version)</span><br><span class="line">	err = cli.ImageTag(ctx, sourceImageName, imageName)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;image tag fail, err=%v&quot;</span>, err.Error())</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">// todo docker rmi $(docker images -f &quot;dangling=true&quot; -q) -f</span></span><br><span class="line"></span><br><span class="line">	<span class="comment">// 3. push image to harbor</span></span><br><span class="line">	log.Printf(<span class="string">&quot;Push image %s to Harbor...\n&quot;</span>, imageName)</span><br><span class="line">	_, err = cli.RegistryLogin(ctx, types.AuthConfig&#123;</span><br><span class="line">		<span class="comment">// todo security</span></span><br><span class="line">		Username:      <span class="string">&quot;admin&quot;</span>,</span><br><span class="line">		Password:      <span class="string">&quot;admin&quot;</span>,</span><br><span class="line">		ServerAddress: harborUrl,</span><br><span class="line">	&#125;)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;push to harbor fail, err=%v&quot;</span>, err.Error())</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	out, err := cli.ImagePush(ctx, imageName, types.ImagePushOptions&#123;&#125;)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;Pulling image failed, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">defer</span> out.Close()</span><br><span class="line">	io.Copy(logFile, out)</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// remove possible &quot;/&quot;</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">getRealVersion</span><span class="params">(rawVersion <span class="type">string</span>)</span></span> <span class="type">string</span> &#123;</span><br><span class="line">	parts := strings.Split(rawVersion, <span class="string">&quot;/&quot;</span>)</span><br><span class="line">	<span class="keyword">if</span> <span class="built_in">len</span>(parts) &gt; <span class="number">0</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> parts[<span class="built_in">len</span>(parts)<span class="number">-1</span>]</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> version</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">getSourceImageName</span><span class="params">(team, project, version <span class="type">string</span>)</span></span> <span class="type">string</span> &#123;</span><br><span class="line">	<span class="keyword">return</span> fmt.Sprintf(<span class="string">&quot;%s/%s/%s:%s&quot;</span>, team, harborRepo, project, version)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">getHarborImageName</span><span class="params">(harborUrl, harborRepo, project, version <span class="type">string</span>)</span></span> <span class="type">string</span> &#123;</span><br><span class="line">	<span class="keyword">return</span> fmt.Sprintf(<span class="string">&quot;%s/%s/%s:%s&quot;</span>, harborUrl, harborRepo, project, version)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="通过SSH通知目标服务器部署"><a href="#通过SSH通知目标服务器部署" class="headerlink" title="通过SSH通知目标服务器部署"></a>通过SSH通知目标服务器部署</h2><p>这里使用<strong>Publish Over SSH</strong>。</p>
<p><strong>部署端</strong>的执行脚本：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">harbor_url=$1</span><br><span class="line">harbor_repo=$2</span><br><span class="line">project=$3</span><br><span class="line">version=$4</span><br><span class="line">container_port=$5</span><br><span class="line"></span><br><span class="line">image_name=$harbor_url/$harbor_repo/$project:$version</span><br><span class="line"></span><br><span class="line">container_id=$(docker ps -a | grep &quot;$&#123;image_name&#125;&quot; | awk &#x27;&#123;print $1&#125;&#x27;)</span><br><span class="line"></span><br><span class="line">echo &quot;Check if container of $&#123;image_name&#125; exists...&quot;</span><br><span class="line">if [ &quot;$&#123;container_id&#125;&quot; != &quot;&quot; ] ; then</span><br><span class="line">  echo &quot;Container $&#123;container_id&#125; of $&#123;image_name&#125; is running, now try to kill it...&quot;</span><br><span class="line">  docker stop &quot;$container_id&quot;</span><br><span class="line">  docker rm &quot;$container_id&quot;</span><br><span class="line">fi</span><br><span class="line"></span><br><span class="line">old_image=$(docker images | grep &quot;$&#123;image_name&#125;&quot;)</span><br><span class="line"></span><br><span class="line">if [ &quot;$&#123;old_image&#125;&quot; != &quot;&quot; ] ; then</span><br><span class="line">  echo &quot;Image $image_name exists, now try to remove it&quot;</span><br><span class="line">  docker rmi &quot;$image_name&quot; -f</span><br><span class="line">fi</span><br><span class="line"></span><br><span class="line">echo &quot;Try to login Harbor...&quot;</span><br><span class="line">docker login -u amdin -p admin &quot;$harbor_url&quot;</span><br><span class="line">echo &quot;Login Harbor successfully.&quot;</span><br><span class="line"></span><br><span class="line">echo &quot;Try to pull $image_name from Harbor...&quot;</span><br><span class="line">docker pull &quot;$&#123;image_name&#125;&quot;</span><br><span class="line">echo &quot;Pull image $&#123;image_name&#125; from Harbor successfully.&quot;</span><br><span class="line"></span><br><span class="line">docker run -d -p &quot;$container_port&quot; --name &quot;$harbor_repo.$project.$version&quot; &quot;$image_name&quot;</span><br><span class="line"></span><br><span class="line">echo &quot;Deploy service successfully.&quot;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>以上脚本执行了如下事情：</p>
<ol>
<li>检查旧版本的容器是否在运行，如果有停止并删除；</li>
<li>检查旧镜像是否存在，如果有删除；</li>
<li>登录私服Harbor</li>
<li>拉取最新的镜像</li>
<li>运行容器</li>
</ol>
<p><strong>发布端（Jenkins）</strong>的执行脚本</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd deploy &amp;&amp; touch deploy.log &amp;&amp; /home/jsy/devops/docker_workdir/deploy.sh $harbor_url $harbor_repo $JOB_NAME $branch_tag $container_port &gt; deploy.log</span><br></pre></td></tr></table></figure>



<p>测试一下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd deploy &amp;&amp; touch deploy.log &amp;&amp; /home/jsy/devops/docker_workdir/deploy.sh 211.87.224.233:8930 sdu-weblab springboot-helloworld origin/master 8080 &gt; deploy.log</span><br></pre></td></tr></table></figure>





<p>使用sshPublisher生成流水线脚本：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sshPublisher(publishers: [sshPublisherDesc(configName: &#x27;sdu-weblab-local-machine&#x27;, transfers: [sshTransfer(cleanRemote: false, excludes: &#x27;&#x27;, execCommand: &quot;cd deploy &amp;&amp; touch deploy.log &amp;&amp; /home/jsy/devops/docker_workdir/deploy.sh $harbor_url $harbor_repo $JOB_NAME $branch_tag $container_port &gt; deploy.log&quot;, execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: &#x27;[, ]+&#x27;, remoteDirectory: &#x27;&#x27;, remoteDirectorySDF: false, removePrefix: &#x27;&#x27;, sourceFiles: &#x27;&#x27;)], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])</span><br></pre></td></tr></table></figure>



<p>stage为</p>
<pre><code>        stage(&#39;Stage 5: Publish over SSH&#39;) &#123;
            steps &#123;
                sshPublisher(publishers: [sshPublisherDesc(configName: &#39;sdu-weblab-local-machine&#39;, transfers: [sshTransfer(cleanRemote: false, excludes: &#39;&#39;, execCommand: &quot;cd deploy &amp;&amp; touch deploy.log &amp;&amp; /home/jsy/devops/docker_workdir/deploy.sh $harbor_url $harbor_repo $JOB_NAME $branch_tag $container_port &gt; deploy.log&quot;, execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: &#39;[, ]+&#39;, remoteDirectory: &#39;&#39;, remoteDirectorySDF: false, removePrefix: &#39;&#39;, sourceFiles: &#39;&#39;)], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
                echo &#39;Stage 5: Publish over SSH -- SUCCESS&#39;
            &#125;
        &#125;
</code></pre>
<h2 id="通过SSH通知目标服务器部署（部署端使用go部署）"><a href="#通过SSH通知目标服务器部署（部署端使用go部署）" class="headerlink" title="通过SSH通知目标服务器部署（部署端使用go部署）"></a>通过SSH通知目标服务器部署（部署端使用go部署）</h2><h3 id="部署程序"><a href="#部署程序" class="headerlink" title="部署程序"></a>部署程序</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	<span class="string">&quot;context&quot;</span></span><br><span class="line">	<span class="string">&quot;errors&quot;</span></span><br><span class="line">	<span class="string">&quot;flag&quot;</span></span><br><span class="line">	<span class="string">&quot;fmt&quot;</span></span><br><span class="line">	<span class="string">&quot;io&quot;</span></span><br><span class="line">	<span class="string">&quot;log&quot;</span></span><br><span class="line">	<span class="string">&quot;os&quot;</span></span><br><span class="line">	<span class="string">&quot;os/exec&quot;</span></span><br><span class="line">	<span class="string">&quot;strconv&quot;</span></span><br><span class="line">	<span class="string">&quot;strings&quot;</span></span><br><span class="line">	<span class="string">&quot;time&quot;</span></span><br><span class="line"></span><br><span class="line">	<span class="string">&quot;github.com/docker/docker/api/types&quot;</span></span><br><span class="line">	<span class="string">&quot;github.com/docker/docker/api/types/container&quot;</span></span><br><span class="line">	<span class="string">&quot;github.com/docker/docker/client&quot;</span></span><br><span class="line">	<span class="string">&quot;github.com/docker/go-connections/nat&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> (</span><br><span class="line">	DefaultVersion       = <span class="string">&quot;master&quot;</span></span><br><span class="line">	MonitorProgrammeName = <span class="string">&quot;monitor&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">	harborUrl     <span class="type">string</span></span><br><span class="line">	harborRepo    <span class="type">string</span></span><br><span class="line">	project       <span class="type">string</span></span><br><span class="line">	version       <span class="type">string</span></span><br><span class="line">	containerPort <span class="type">int</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">	logFileName <span class="type">string</span></span><br><span class="line">	containerID <span class="type">string</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">init</span><span class="params">()</span></span> &#123;</span><br><span class="line">	logFileName = time.Now().Format(<span class="string">&quot;2006-01-02 15:04:05&quot;</span>) + <span class="string">&quot;.log&quot;</span></span><br><span class="line">	logFile, err := os.OpenFile(logFileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, <span class="number">0644</span>)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		<span class="built_in">panic</span>(err)</span><br><span class="line">	&#125;</span><br><span class="line">	log.SetOutput(logFile)</span><br><span class="line">	log.SetPrefix(<span class="string">&quot;[sdu-weblab-deploy]&quot;</span>)</span><br><span class="line">	log.SetFlags(log.LstdFlags | log.Lshortfile | log.LUTC)</span><br><span class="line">	<span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	flag.StringVar(&amp;harborUrl, <span class="string">&quot;harbor_url&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;harborRepo, <span class="string">&quot;harbor_repo&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;project, <span class="string">&quot;project&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;version, <span class="string">&quot;version&quot;</span>, DefaultVersion, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.IntVar(&amp;containerPort, <span class="string">&quot;container_port&quot;</span>, <span class="number">8080</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.Parse()</span><br><span class="line">	<span class="keyword">if</span> <span class="built_in">len</span>(harborUrl) == <span class="number">0</span> || <span class="built_in">len</span>(harborRepo) == <span class="number">0</span> || <span class="built_in">len</span>(project) == <span class="number">0</span> &#123;</span><br><span class="line">		log.Fatal(<span class="string">&quot;key args missing&quot;</span>)</span><br><span class="line">	&#125;</span><br><span class="line">	log.Printf(<span class="string">&quot;Notice: harbor_url=%s, harbor_repo=%s, project=%s, version=%s, container_port=%d&quot;</span>,</span><br><span class="line">		harborUrl, harborRepo, project, version, containerPort)</span><br><span class="line"></span><br><span class="line">	version = getBranch(version)</span><br><span class="line">	imageName := harborUrl + <span class="string">&quot;/&quot;</span> + harborRepo + <span class="string">&quot;/&quot;</span> + project + <span class="string">&quot;:&quot;</span> + version</span><br><span class="line">	log.Printf(<span class="string">&quot;Docker image name is %s&quot;</span>, imageName)</span><br><span class="line"></span><br><span class="line">	ctx := context.Background()</span><br><span class="line">	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;New docker client fail, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 1. check existing containers</span></span><br><span class="line">	log.Printf(<span class="string">&quot;Check if container of %s exists...\n&quot;</span>, imageName)</span><br><span class="line">	containers, err := cli.ContainerList(ctx, types.ContainerListOptions&#123;All: <span class="literal">true</span>&#125;)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;List all containers fail, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">var</span> timeOut = time.Second</span><br><span class="line">	<span class="keyword">for</span> _, c := <span class="keyword">range</span> containers &#123;</span><br><span class="line">		<span class="keyword">if</span> c.Image == imageName &#123;</span><br><span class="line">			cid := c.ID</span><br><span class="line">			err = cli.ContainerStop(ctx, cid, &amp;timeOut)</span><br><span class="line">			<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">				log.Printf(<span class="string">&quot;Warning: container.ID=%s stop failed&quot;</span>, cid)</span><br><span class="line">			&#125;</span><br><span class="line">			err = cli.ContainerRemove(ctx, cid, types.ContainerRemoveOptions&#123;Force: <span class="literal">true</span>&#125;)</span><br><span class="line">			<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">				log.Fatalf(<span class="string">&quot;Container.ID=%s remove failed&quot;</span>, cid)</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 2. check outdated version images</span></span><br><span class="line">	log.Printf(<span class="string">&quot;Check if image %s exists...\n&quot;</span>, imageName)</span><br><span class="line">	imageList, err := cli.ImageList(ctx, types.ImageListOptions&#123;</span><br><span class="line">		All: <span class="literal">true</span>,</span><br><span class="line">	&#125;)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;List all images fail, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">for</span> _, image := <span class="keyword">range</span> imageList &#123;</span><br><span class="line">		<span class="keyword">for</span> _, repoTag := <span class="keyword">range</span> image.RepoTags &#123;</span><br><span class="line">			<span class="keyword">if</span> repoTag == imageName &#123;</span><br><span class="line">				cli.ImageRemove(ctx, image.ID, types.ImageRemoveOptions&#123;</span><br><span class="line">					Force: <span class="literal">true</span>,</span><br><span class="line">				&#125;)</span><br><span class="line">				<span class="keyword">break</span></span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 3. pull image from harbor</span></span><br><span class="line">	log.Printf(<span class="string">&quot;Pull image %s from Harbor...\n&quot;</span>, imageName)</span><br><span class="line">	_, err = cli.RegistryLogin(ctx, types.AuthConfig&#123;</span><br><span class="line">		<span class="comment">// todo security</span></span><br><span class="line">		Username:      <span class="string">&quot;admin&quot;</span>,</span><br><span class="line">		Password:      <span class="string">&quot;admin&quot;</span>,</span><br><span class="line">		ServerAddress: harborUrl,</span><br><span class="line">	&#125;)</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		<span class="built_in">panic</span>(<span class="literal">nil</span>)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// out 是响应输出流，可以不输出</span></span><br><span class="line">	out, err := cli.ImagePull(ctx, imageName, types.ImagePullOptions&#123;&#125;)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		<span class="built_in">panic</span>(err)</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">defer</span> out.Close()</span><br><span class="line">	io.Copy(os.Stdout, out)</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 4. docker run -d -p &quot;$container_port&quot; --name &quot;$harbor_repo.$project.$version&quot; &quot;$image_name&quot;</span></span><br><span class="line">	log.Printf(<span class="string">&quot;Run image %s to be contaniner...\n&quot;</span>, imageName)</span><br><span class="line">	containerName := harborRepo + <span class="string">&quot;.&quot;</span> + project + <span class="string">&quot;.&quot;</span> + version</span><br><span class="line">	containerNatPort := nat.Port(strconv.Itoa(containerPort) + <span class="string">&quot;/tcp&quot;</span>)</span><br><span class="line"></span><br><span class="line">	resp, err := cli.ContainerCreate(ctx,</span><br><span class="line">		&amp;container.Config&#123;</span><br><span class="line">			Image: imageName,</span><br><span class="line">			ExposedPorts: nat.PortSet&#123;</span><br><span class="line">				containerNatPort: <span class="keyword">struct</span>&#123;&#125;&#123;&#125;,</span><br><span class="line">			&#125;,</span><br><span class="line">		&#125;, &amp;container.HostConfig&#123;</span><br><span class="line">			PublishAllPorts: <span class="literal">true</span>,</span><br><span class="line">		&#125;, <span class="literal">nil</span>, <span class="literal">nil</span>, containerName)</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;Creating container failed, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">if</span> err = cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions&#123;&#125;); err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;Starting container failed, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	containerID = resp.ID</span><br><span class="line">	log.Printf(<span class="string">&quot;Container ID = %s&quot;</span>, containerID)</span><br><span class="line"></span><br><span class="line">	<span class="comment">// todo register to center</span></span><br><span class="line">	hostPortOfContainerPort, err := getHostPortOfContainerPort(containerID, strconv.Itoa(containerPort))</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Printf(<span class="string">&quot;Error: get port binding failed, err=%v&quot;</span>, err)</span><br><span class="line">	&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">		log.Printf(<span class="string">&quot;Container hostPort of container port is %s&quot;</span>, hostPortOfContainerPort)</span><br><span class="line">	&#125;</span><br><span class="line">	log.Println(<span class="string">&quot;Deploy successfully.&quot;</span>)</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 5. run a new programme to fetch log stream and transfer it to websocket</span></span><br><span class="line">	runGoProgramme(MonitorProgrammeName)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">runGoProgramme</span><span class="params">(programme <span class="type">string</span>)</span></span> &#123;</span><br><span class="line">	log.Printf(<span class="string">&quot;Next to run programme %s &quot;</span>, programme)</span><br><span class="line">	sduWeblabBinHome := os.Getenv(<span class="string">&quot;SDU_WEBLAB_BIN_HOME&quot;</span>)</span><br><span class="line">	s := fmt.Sprintf(<span class="string">&quot;%s/%s --logFileName=%s --containerID=%s&quot;</span>, sduWeblabBinHome, programme, logFileName, containerID)</span><br><span class="line">	log.Printf(<span class="string">&quot;Shell script is %s&quot;</span>, s)</span><br><span class="line">	err := exec.Command(<span class="string">&quot;/bin/bash&quot;</span>, <span class="string">&quot;-c&quot;</span>, s).Start()</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;Fail to run programe %s, err=%v&quot;</span>, programme, err)</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">getBranch</span><span class="params">(repoBranch <span class="type">string</span>)</span></span> <span class="type">string</span> &#123;</span><br><span class="line">	parts := strings.Split(repoBranch, <span class="string">&quot;/&quot;</span>)</span><br><span class="line">	<span class="keyword">if</span> <span class="built_in">len</span>(parts) &gt; <span class="number">0</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> parts[<span class="built_in">len</span>(parts)<span class="number">-1</span>]</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> DefaultVersion</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">getHostPortOfContainerPort</span><span class="params">(containerId <span class="type">string</span>, containerPort <span class="type">string</span>)</span></span> (port <span class="type">string</span>, err <span class="type">error</span>) &#123;</span><br><span class="line">	shell := <span class="string">&quot;docker port &quot;</span> + containerId + <span class="string">&quot; | grep &quot;</span> + containerPort + <span class="string">&quot; | awk &#x27;&#123;print $3&#125;&#x27;&quot;</span></span><br><span class="line">	output, err := exec.Command(<span class="string">&quot;bash&quot;</span>, <span class="string">&quot;-c&quot;</span>, shell).Output()</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		<span class="keyword">return</span></span><br><span class="line">	&#125;</span><br><span class="line">	shellOut := <span class="type">string</span>(output)</span><br><span class="line">	parts := strings.Split(shellOut, <span class="string">&quot;\n&quot;</span>)</span><br><span class="line">	<span class="keyword">if</span> <span class="built_in">len</span>(parts) == <span class="number">0</span> &#123;</span><br><span class="line">		err = fmt.Errorf(<span class="string">&quot;containerPort=%s has no bindings&quot;</span>, containerPort)</span><br><span class="line">		<span class="keyword">return</span></span><br><span class="line">	&#125;</span><br><span class="line">	ipPort := parts[<span class="number">0</span>]</span><br><span class="line">	splits := strings.Split(ipPort, <span class="string">&quot;:&quot;</span>)</span><br><span class="line">	<span class="keyword">if</span> <span class="built_in">len</span>(splits) != <span class="number">2</span> &#123;</span><br><span class="line">		err = errors.New(<span class="string">&quot;invalid format&quot;</span>)</span><br><span class="line">		<span class="keyword">return</span></span><br><span class="line">	&#125;</span><br><span class="line">	port = splits[<span class="number">1</span>]</span><br><span class="line">	<span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="监控程序"><a href="#监控程序" class="headerlink" title="监控程序"></a>监控程序</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	<span class="string">&quot;bufio&quot;</span></span><br><span class="line">	<span class="string">&quot;context&quot;</span></span><br><span class="line">	<span class="string">&quot;flag&quot;</span></span><br><span class="line">	<span class="string">&quot;io&quot;</span></span><br><span class="line">	<span class="string">&quot;log&quot;</span></span><br><span class="line">	<span class="string">&quot;net&quot;</span></span><br><span class="line">	<span class="string">&quot;net/http&quot;</span></span><br><span class="line">	<span class="string">&quot;os&quot;</span></span><br><span class="line"></span><br><span class="line">	<span class="string">&quot;github.com/docker/docker/api/types&quot;</span></span><br><span class="line">	<span class="string">&quot;github.com/docker/docker/client&quot;</span></span><br><span class="line">	<span class="string">&quot;github.com/gorilla/websocket&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">	logFileName <span class="type">string</span></span><br><span class="line">	containerID <span class="type">string</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">	logOut   io.ReadCloser</span><br><span class="line">	upgrader = websocket.Upgrader&#123;</span><br><span class="line">		CheckOrigin: <span class="function"><span class="keyword">func</span><span class="params">(r *http.Request)</span></span> <span class="type">bool</span> &#123;</span><br><span class="line">			<span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">		&#125;,</span><br><span class="line">	&#125; <span class="comment">// use default options</span></span><br><span class="line">	conn       *websocket.Conn</span><br><span class="line">	listenPort <span class="type">int</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">InitLog</span><span class="params">()</span></span> &#123;</span><br><span class="line">	logFile, err := os.OpenFile(logFileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, <span class="number">0644</span>)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		<span class="built_in">panic</span>(err)</span><br><span class="line">	&#125;</span><br><span class="line">	log.SetOutput(logFile)</span><br><span class="line">	log.SetPrefix(<span class="string">&quot;[sdu-weblab-deploy]&quot;</span>)</span><br><span class="line">	log.SetFlags(log.LstdFlags | log.Lshortfile | log.LUTC)</span><br><span class="line">	<span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	<span class="comment">// fetch log stream and transfer it to websocket</span></span><br><span class="line">	flag.StringVar(&amp;logFileName, <span class="string">&quot;logFileName&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;containerID, <span class="string">&quot;containerID&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.Parse()</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> <span class="built_in">len</span>(logFileName) == <span class="number">0</span> || <span class="built_in">len</span>(containerID) == <span class="number">0</span> &#123;</span><br><span class="line">		log.Fatal(<span class="string">&quot;key args missing&quot;</span>)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// init log settings</span></span><br><span class="line">	InitLog()</span><br><span class="line"></span><br><span class="line">	log.Printf(<span class="string">&quot;Fetch log stream and transfer it to websocket...\n&quot;</span>)</span><br><span class="line">	ctx := context.Background()</span><br><span class="line">	<span class="keyword">var</span> err <span class="type">error</span></span><br><span class="line">	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;New docker client fail, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	options := types.ContainerLogsOptions&#123;</span><br><span class="line">		ShowStdout: <span class="literal">true</span>,</span><br><span class="line">		ShowStderr: <span class="literal">true</span>,</span><br><span class="line">		Follow:     <span class="literal">true</span>,</span><br><span class="line">	&#125;</span><br><span class="line">	logOut, err = cli.ContainerLogs(ctx, containerID, options) <span class="comment">// remote</span></span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;Get log of container failed, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	listener, err := net.Listen(<span class="string">&quot;tcp&quot;</span>, <span class="string">&quot;:0&quot;</span>)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;Net listen failed, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line">	listenPort = listener.Addr().(*net.TCPAddr).Port</span><br><span class="line">	<span class="comment">// todo register to center</span></span><br><span class="line">	log.Printf(<span class="string">&quot;Websocket port is %d&quot;</span>, listenPort)</span><br><span class="line"></span><br><span class="line">	http.HandleFunc(<span class="string">&quot;/logs&quot;</span>, socketHandler)</span><br><span class="line">	log.Fatal(http.Serve(listener, <span class="literal">nil</span>))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">socketHandler</span><span class="params">(w http.ResponseWriter, r *http.Request)</span></span> &#123;</span><br><span class="line">	<span class="comment">// Upgrade our raw HTTP connection to a websocket based one</span></span><br><span class="line">	_conn, err := upgrader.Upgrade(w, r, <span class="literal">nil</span>)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Print(<span class="string">&quot;Error during connection upgradation:&quot;</span>, err)</span><br><span class="line">		os.Exit(<span class="number">3</span>)</span><br><span class="line">	&#125;</span><br><span class="line">	conn = _conn</span><br><span class="line">	<span class="keyword">defer</span> conn.Close()</span><br><span class="line"></span><br><span class="line">	reader := bufio.NewReader(logOut)</span><br><span class="line">	<span class="comment">// The event loop</span></span><br><span class="line">	<span class="keyword">for</span> &#123;</span><br><span class="line">		message, err := reader.ReadString(<span class="string">&#x27;\n&#x27;</span>)</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">			<span class="keyword">if</span> err == io.EOF &#123;</span><br><span class="line">				exitAndNotifyPeer(message, <span class="number">0</span>)</span><br><span class="line">			&#125;</span><br><span class="line">			exitAndNotifyPeer(<span class="string">&quot;\n&quot;</span>, <span class="number">1</span>)</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="comment">//log.Printf(&quot;Log read is: %s&quot;, message)</span></span><br><span class="line">		err = conn.WriteMessage(websocket.TextMessage, []<span class="type">byte</span>(message))</span><br><span class="line">		<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">			log.Println(<span class="string">&quot;Error during message writing: &quot;</span>, err)</span><br><span class="line">			os.Exit(<span class="number">2</span>)</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">exitAndNotifyPeer</span><span class="params">(msg <span class="type">string</span>, exitCode <span class="type">int</span>)</span></span> &#123;</span><br><span class="line">	conn.WriteMessage(websocket.CloseMessage, []<span class="type">byte</span>(msg))</span><br><span class="line">	os.Exit(exitCode)</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>





<h3 id="构建脚本"><a href="#构建脚本" class="headerlink" title="构建脚本"></a>构建脚本</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">jsy@wzai:~/devops/docker_workdir$ tree</span><br><span class="line">.</span><br><span class="line">├── deploy</span><br><span class="line">│   ├── go.mod</span><br><span class="line">│   ├── go.sum</span><br><span class="line">│   ├── main.go</span><br><span class="line">│   └── output</span><br><span class="line">│       └── bin</span><br><span class="line">│           └── deploy</span><br><span class="line">├── monitor</span><br><span class="line">│   ├── go.mod</span><br><span class="line">│   ├── go.sum</span><br><span class="line">│   ├── main.go</span><br><span class="line">│   └── output</span><br><span class="line">│       └── bin</span><br><span class="line">│           └── monitor</span><br></pre></td></tr></table></figure>

<p><code>go build</code></p>
<p>首次构建</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cd ~/devops/docker_workdir/</span><br><span class="line">cd deploy &amp;&amp; go mod tidy &amp;&amp; mkdir -p output/bin &amp;&amp; go build -o output/bin/deploy</span><br><span class="line">cd monitor &amp;&amp; go mod tidy &amp;&amp; mkdir -p output/bin &amp;&amp; go build -o output/bin/monitor</span><br></pre></td></tr></table></figure>

<p>后面更新</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">cd ~/devops/docker_workdir/</span><br><span class="line">cd deploy &amp;&amp; go mod tidy &amp;&amp; go build -o output/bin/deploy</span><br><span class="line">cd ~/devops/docker_workdir/</span><br><span class="line">cd monitor &amp;&amp; go mod tidy &amp;&amp; go build -o output/bin/monitor</span><br></pre></td></tr></table></figure>



<p>构建或更新软链接</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo ln -f -s /home/jsy/devops/docker_workdir/deploy/output/bin/deploy /opt/sduweblab/bin/deploy</span><br><span class="line">sudo ln -f -s /home/jsy/devops/docker_workdir/monitor/output/bin/monitor /opt/sduweblab/bin/monitor</span><br></pre></td></tr></table></figure>





<p><del>更新产物</del></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">cd ~/devops/docker_workdir/</span><br><span class="line">cd deploy &amp;&amp; go mod tidy &amp;&amp; go build -o output/bin/deploy</span><br><span class="line">cd ~/devops/docker_workdir/</span><br><span class="line">cd monitor &amp;&amp; go mod tidy &amp;&amp; go build -o output/bin/monitor</span><br><span class="line">sudo ln -f -s /home/jsy/devops/docker_workdir/deploy/output/bin/deploy /opt/sduweblab/bin/deploy</span><br><span class="line">sudo ln -f -s /home/jsy/devops/docker_workdir/monitor/output/bin/monitor /opt/sduweblab/bin/monitor</span><br></pre></td></tr></table></figure>


<p>更新产物（推送镜像到harbor也使用go改写）</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">sudo rm /opt/sduweblab/bin/deploy</span><br><span class="line">sudo rm /opt/sduweblab/bin/monitor</span><br><span class="line">sudo rm /opt/sduweblab/bin/push_to_harbor</span><br><span class="line">cd ~/devops/docker_workdir/</span><br><span class="line">cd deploy &amp;&amp; go mod tidy &amp;&amp; go build -o output/bin/deploy</span><br><span class="line">cd ~/devops/docker_workdir/</span><br><span class="line">cd monitor &amp;&amp; go mod tidy &amp;&amp; go build -o output/bin/monitor</span><br><span class="line">cd ~/devops/docker_workdir/</span><br><span class="line">cd push_to_harbor &amp;&amp; go mod tidy &amp;&amp; go build -o output/bin/push_to_harbor</span><br><span class="line">sudo cp /home/jsy/devops/docker_workdir/deploy/output/bin/deploy /opt/sduweblab/bin/deploy</span><br><span class="line">sudo cp /home/jsy/devops/docker_workdir/monitor/output/bin/monitor /opt/sduweblab/bin/monitor</span><br><span class="line">sudo cp /home/jsy/devops/docker_workdir/push_to_harbor/output/bin/push_to_harbor /opt/sduweblab/bin/push_to_harbor</span><br><span class="line">cd</span><br></pre></td></tr></table></figure>





<p>原先的shell换成go程序即可。</p>
<p>测试脚本：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd deploy &amp;&amp; /opt/sduweblab/bin/deploy --harbor_url=<span class="number">211.87</span><span class="number">.224</span><span class="number">.233</span>:<span class="number">8930</span> --harbor_repo=sdu-weblab --project=springboot-helloworld --version=origin/master --container_port=<span class="number">8080</span></span><br></pre></td></tr></table></figure>


    </div>

    
    
    

    <footer class="post-footer">

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/2022/05/06/Sdu-Devops-01/" rel="prev" title="安装Docker、Harbor、Jenkins、Gitlab">
                  <i class="fa fa-chevron-left"></i> 安装Docker、Harbor、Jenkins、Gitlab
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/2022/05/08/Git%E7%9A%84%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/" rel="next" title="Git的学习笔记">
                  Git的学习笔记 <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">SongyangJi</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>
  <div class="sidebar-dimmer"></div>
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>





  





</body>
</html>
